#ifndef DEPTHOFFILED_H
#define DEPTHOFFILED_H

#include <glm/glm.hpp>
#include "Shader.h"
#include "RenderTarget.h"
#include "Model.h"
#include "RenderContex.h"
#include "RenderEngine.h"

#include "Helper.h"

using glm::vec2;
using glm::vec3;

class DepthOfField
{
public:
	DepthOfField()
	{

	}

	~DepthOfField()
	{
	}

	static void static_init()
	{
		shader_coc.init(IOS_PATH("shader", "Quad", "vert"), IOS_PATH("shader", "DepthOfField_coc", "frag"));
		shader_blur.init(IOS_PATH("shader", "Quad", "vert"), IOS_PATH("shader", "DepthOfField_blur", "frag"));
	}

	void init(float focusDistance, float focusRange, const glm::vec2 &focusFalloff, float blurWidth)
	{
		_focus_distance = focusDistance;
		_focus_range = focusRange;
		_focus_falloff = focusFalloff;
		_blur_width = blurWidth;

		int w = RenderContex::window_width;
		int h = RenderContex::window_height;
		_rt_temp.init(w, h, GL_SRGB8_ALPHA8);
		_rt_coc.init(w, h , GL_R8, GL_RED);
	}

	void resize(int width, int height)
	{
		_rt_temp.resize(width, height);
		_rt_coc.resize(width, height);
	}

	void render(RenderTexture & src, RenderTexture & dst, RenderTexture & depth_texture)
	{
		glViewport(0, 0, RenderContex::window_width, RenderContex::window_height);
		coc(depth_texture, _rt_coc);
		blur(src, _rt_temp, _blur_width, dof_blur_horizon); // (1, 0)
		blur(_rt_temp, dst, _blur_width, dof_blur_vertical); // (0, 1)
	}

	void set_focus_range(float focus_range)
	{
		_focus_range = focus_range;
	}

	void set_focus_falloff(float focus_falloff)
	{
		_focus_falloff.x = _focus_falloff.y = focus_falloff;
	}

	void set_focus_distance(float focus_distance)
	{
		_focus_distance = focus_distance;
	}

private:

	enum dof_blur_mode{dof_blur_horizon, dof_blur_vertical};

	void blur(RenderTexture & src, RenderTexture & dst, float blur_width, dof_blur_mode mode)
	{
		shader_blur.use();
		vec2 pixel_size(1.0f / RenderContex::window_width, 1.0f / RenderContex::window_height);
		vec2 step = pixel_size * blur_width;
		//shader_blur.bind_vec2("pixelSize", pixel_size);
		//shader_blur.bind_float("blurWidth", blur_width);
		if (dof_blur_vertical == mode)
			step *= glm::vec2(0, 1);
		else
			step *= glm::vec2(1, 0);
		shader_blur.bind_vec2("step", step);
		shader_blur.bind_texture("cocTex", _rt_coc, 0);
		shader_blur.bind_texture("blurredTex", src, 1);

		RenderEngine::set_render_target(&dst, nullptr);
		ModelManager::screen_aligned_quad.render();
		RenderEngine::set_render_target(nullptr, nullptr);
	}

	void coc(RenderTexture & depth_texture, RenderTexture & dst)
	{
		shader_coc.use();
		
		shader_coc.bind_float("focusDistance", _focus_distance);
		shader_coc.bind_float("focusRange", _focus_range);
		shader_coc.bind_vec2("focusFalloff", _focus_falloff);
		shader_coc.bind_texture("depthTex", depth_texture, 0);
		
		RenderEngine::set_render_target(&dst, nullptr);
		ModelManager::screen_aligned_quad.render();
		RenderEngine::set_render_target(nullptr, nullptr);
	}

	//int _width;
	//int _height;
	float _blur_width;
	float _focus_distance;
	float _focus_range;
	glm::vec2 _focus_falloff;

private:
	static Shader shader_coc;
	static Shader shader_blur;

	RenderTexture _rt_temp;
	RenderTexture _rt_coc;
};



#endif